package com.itproject.idgenerator.simple;

import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.SystemUtils;

import java.net.Inet4Address;
import java.net.UnknownHostException;

/**
 * 不依赖yitter，原汁原味的雪花算法ID分布式生成器,根据当前机器的IP和host做机器ID和数据中心ID的生成规则，防止分布式集群环境出现ID重复。
 */
public class IdWorker {
	  private final long twepoch = 1288834974657L;
	  private final long workerIdBits = 5L;  
	  private final long datacenterIdBits = 5L;  
	  private final long maxWorkerId = -1L ^ (-1L << workerIdBits);  
	  private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);  
	  private final long sequenceBits = 12L;  
	  private final long workerIdShift = sequenceBits;  
	  private final long datacenterIdShift = sequenceBits + workerIdBits;  
	  private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;  
	  private final long sequenceMask = -1L ^ (-1L << sequenceBits);  
	   
	  private long workerId;  
	  private long datacenterId;  
	  private long sequence = 0L;  
	  private long lastTimestamp = -1L;  
	  
	  public static IdWorker ID_WORKER;
	  
	  static{
		  ID_WORKER  = new IdWorker(getWorkId(),getDataCenterId());
	  }
	  
	  public IdWorker(long workerId, long datacenterId) {  
	    if (workerId > maxWorkerId || workerId < 0) {  
	      throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));  
	    }  
	    if (datacenterId > maxDatacenterId || datacenterId < 0) {  
	      throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));  
	    }  
	    this.workerId = workerId;  
	    this.datacenterId = datacenterId;  
	  }  
	   
	  public synchronized long nextId() {  
	    long timestamp = timeGen();  
	    if (timestamp < lastTimestamp) {  
	      throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));  
	    }  
	    if (lastTimestamp == timestamp) {  
	      sequence = (sequence + 1) & sequenceMask;  
	      if (sequence == 0) {  
	        timestamp = tilNextMillis(lastTimestamp);  
	      }  
	    } else {  
	      sequence = 0L;  
	    }  
	   
	    lastTimestamp = timestamp;  
	   
	    return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;  
	  }  
	   
	  protected long tilNextMillis(long lastTimestamp) {  
	    long timestamp = timeGen();  
	    while (timestamp <= lastTimestamp) {
	      timestamp = timeGen();  
	    }  
	    return timestamp;  
	  }  
	   
	  protected long timeGen() {  
	    return System.currentTimeMillis();  
	  }  
	   
	  
	  private static Long getWorkId(){
	        try {
	            String hostAddress = Inet4Address.getLocalHost().getHostAddress();
	            int[] ints = StringUtils.toCodePoints(hostAddress);
	            int sums = 0;
	            for(int b : ints){
	                sums += b;
	            }
	            return (long)(sums % 32);
	        } catch (UnknownHostException e) {
	            // 如果获取失败，则使用随机数备用
	            return RandomUtils.nextLong(0,31);
	        }
	    }
	 
	    private static Long getDataCenterId(){
	        int[] ints = StringUtils.toCodePoints(SystemUtils.getHostName());
	        int sums = 0;
	        for (int i: ints) {
	            sums += i;
	        }
	        return (long)(sums % 32);
	    }
	    
	  public static void main(String[] args) {  
		  System.out.println(System.currentTimeMillis());
	      long startTime = System.nanoTime();
	      for (int i = 0; i < 50000; i++) {
	           long id = IdWorker.ID_WORKER.nextId();
	           System.out.println(id);
	      }
	      System.out.println((System.nanoTime()-startTime)/1000000+"ms");
	  }  
}
